using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using Core.Domain.Entities;
using Core.Domain.Enums;
using Core.Interfaces;
using Core.Settings;

namespace TallyJ.Announcer
{
  ///<summary>
  ///  Presenter for the Announcer
  ///</summary>
  internal class AnnouncerPresenter
  {
    private readonly IEnvironmentDataHelper _environmentDataHelper;
    private readonly IAnnouncerView _view;
    private int _currentIndex;
    private bool _done;
    private IEnvironment _environment;

    private List<Person> _personList;

    ///<summary>
    ///  Default. Creates new window.
    ///</summary>
    public AnnouncerPresenter()
      : this(new AnnouncerWindow(), null)
      //public AnnouncerPresenter() : this(new AnnouncerWindow(), IoC.Container.Resolve<IEnvironment>())
    {
    }

    ///<summary>
    ///</summary>
    ///<param name = "view"></param>
    ///<param name = "environment"></param>
    public AnnouncerPresenter(IAnnouncerView view, IEnvironment environment)
    {
      _view = view;
      _view.Presenter = this;

      _environment = environment;
      
      ElectionParticipationMethods = new[]
                                       {
                                         ElectionParticipationMethod.VotedInPerson,
                                         //ElectionParticipationMethod.DroppedOff,
                                         //ElectionParticipationMethod.MailedIn
                                       };
      AnnoucerDataContext = new AnnouncerDataContext();

      _view.LocalDataContext = AnnoucerDataContext;

      // don't interrupt!
      //_environment.Settings.FilePathChanged += OnDataChangedExternally;
      //_environment.CommunityFileChanged += OnDataChangedExternally;

      SetCurrent(-1);
    }

    private AnnouncerDataContext AnnoucerDataContext { get; set; }

    ///<summary>
    ///  The type of participation to show on the screen. Defaults to <see cref = "ElectionParticipationMethod.VotedInPerson" />
    ///</summary>
    public ElectionParticipationMethod[] ElectionParticipationMethods { get; set; }

    ///<summary>
    ///  List of people to show
    ///</summary>
    public List<Person> PersonList
    {
      get { return _personList ?? (_personList = ExtractPersonList(_environment.Community).ToList()); }
    }

    public IEnvironment Environment
    {
      get { return _environment; }
      set { _environment = value; }
    }

    private void OnDataChangedExternally(object sender, EventArgs eventArgs)
    {
      ReloadElectionInfo();

      // compare the two... add new to the end
      //var comparer = new Person.CompareByVoteeId();
      //var added = false;

      //foreach (var person in newPeople)
      //{
      //  if(!_people.Contains(person, comparer))
      //  {
      //    added = true;
      //    _people.Add(person);
      //  }
      //}
      //if(added)
      //{
      //  _personCount = _people.Count;
      //  SetCurrent(_currentIndex);
      //}
    }

    ///<summary>
    ///  Respond to key strokes
    ///</summary>
    ///<param name = "sender"></param>
    ///<param name = "args"></param>
    public void OnKeyDown(object sender, KeyEventArgs args)
    {
      switch (args.Key)
      {
        case Key.Up:
          SetCurrent(_currentIndex - 1);
          break;
        case Key.Down:
        case Key.Space:
          SetCurrent(_currentIndex + 1);
          break;
        case Key.Escape:
          _view.Close();
          break;
      }
    }

    private void SetCurrent(int i)
    {
      if (_done)
      {
        return;
      }
      if (i < -3)
      {
        i = -3; // allow to overshoot
      }
      else
      {
        var count = PersonList.Count;
        if (i > count)
        {
          i = count;
          DoAnimation(null, new AnimateTypeArg("AnimateDone"));
          _done = true;
        }
        else
        {
          if (_currentIndex != i)
          {
            DoAnimation(null, new AnimateTypeArg(_currentIndex < i ? "AnimateUp" : "AnimateDown"));
          }
        }
      }

      _currentIndex = i;

      AnnoucerDataContext.PastPerson1 = GetPerson(i - 1);
      AnnoucerDataContext.Current = GetPerson(i);
      AnnoucerDataContext.UpcomingPerson1 = GetPerson(i + 1);
      AnnoucerDataContext.UpcomingPerson2 = GetPerson(i + 2);
      AnnoucerDataContext.UpcomingPerson3 = GetPerson(i + 3);
    }

    private Person GetPerson(int i)
    {
      if (PersonList == null)
      {
        return null;
      }

      if (i < 0 || i > PersonList.Count - 1)
      {
        return null;
      }
      return PersonList[i];
    }

    private void ReloadElectionInfo()
    {
      _personList = null; // force a reload

      SetCurrent(-1);
    }

    private IEnumerable<Person> ExtractPersonList(ICommunity community)
    {
      return community.People
        .Where(p => ElectionParticipationMethods.Contains(p.ElectionParticipationMethod))
        .OrderBy(p => p.SortableName());
    }

    ///<summary>
    ///  Start this view
    ///</summary>
    public void Show()
    {
      _view.Show();
    }

    ///<summary>
    ///  Start this view as a dialog
    ///</summary>
    public void ShowDialog()
    {
      _view.ShowDialog();
    }

    ///<summary>
    ///  respond to mouse
    ///</summary>
    ///<param name = "sender"></param>
    ///<param name = "e"></param>
    public void OnMouseWheel(object sender, MouseWheelEventArgs e)
    {
      SetCurrent(_currentIndex + (e.Delta < 0
                                    ? 1
                                    : (e.Delta > 0
                                         ? -1
                                         : 0)));
    }

    ///<summary>
    ///</summary>
    internal event EventHandler<AnimateTypeArg> DoAnimation;
  }
}